﻿using System;
using System.Collections.Generic;
using EventFlowLibrary.LevelComponents;
using Microsoft.Xna.Framework;
using PerformanceUtility.GameDebugTools;
using ScreenManagement;
using LevelComponents;
using Nine.Animations;
using Nine.Graphics;
using System.Text;
using PerformanceUtility.ShapeRendering;
using System.Globalization;

namespace EventFlowLibrary.GameStructure
{
    // TODO move all enums to enum class
    internal enum GameEventType
    {
        OnIntersect,
        WhileIntersect,
        ExitIntersect,
        OnOrientation,
        WhileOrientation,
        ExitOrientation,
    }

    /// <summary>
    /// Actions triggered by events that we can call.
    /// </summary>
    internal enum GameEventAction
    {
        /// <summary>
        /// Enqueue a text event. Specify ID and whether to allow duplicates.
        /// </summary>
        EnqueueText,
        /// <summary>
        /// Dequeue a specific text event, providing ID and whether to remove all occurences.
        /// </summary>
        DequeueText,
        /// <summary>
        /// Displays a button which on input executes a queued action, such as displaying text.
        /// </summary>
        DisplayActionButton,
        /// <summary>
        /// Loads a specified XML file as the new level.
        /// </summary>
        LoadLevel,
        /// <summary>
        /// An event that changes the camera type to system controlled for a specified camera movement,
        /// then returns control to player with previous camera type when completed.
        /// </summary>
        CameraPath,
        /// <summary>
        /// Changes the game state to the specified successor.
        /// </summary>
        ChangeGameState,
        /// <summary>
        /// Play an audio event
        /// </summary>
        Audio,
        /// <summary>
        /// Change a model to a specified animation.
        /// </summary>
        ChangeAnimation,
        /// <summary>
        /// Move/rotate a specified model
        /// </summary>
        MoveModel,
        /// <summary>
        /// Show a letter with the specified text
        /// </summary>
        ShowLetter,
    }

    /// <summary>
    /// An event that can be triggered in the game world.
    /// </summary>
    internal sealed class GameEvent
    {
        internal GameEventType EventType { get { return eventType; } }
        private GameEventType eventType;

        /// <summary>
        /// The type of event triggered by this event.
        /// </summary>
        internal GameEventAction EventAction { get { return eventAction; } }
        private GameEventAction eventAction;

        /// <summary>
        /// Event arguments provided to call the event.
        /// </summary>
        internal object[] EventArguments { get { return eventArguments.ToArray(); } }
        private List<object> eventArguments = new List<object>();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_action"></param>
        /// <param name="_args"></param>
        internal GameEvent(GameEventType _type, GameEventAction _action, object[] _args)
        {
#if TRACE
            List<string> args = new List<string>();
            for (int i = 0; i < _args.Length; i++)
            {
                args.Add(_args.ToString());
            }
            //DebugSystem.Instance.EventDisplayer.EnqueueDisplayEvent("DisplayActionButton: " + _action.ToString() + " Args: " + args.ToString());
#endif
            eventType = _type;
            eventAction = _action;
            for (int i = 0; i < _args.Length; i++)
            {
                eventArguments.Add(_args[i]);
            }
        }
    }

    /// <summary>
    /// Base class for event collision objects.
    /// </summary>
    internal partial class EventObject
    {
        /// <summary>
        /// The unique ID of the event.
        /// </summary>
        internal string ID { get { return id; } set { id = value; } }
        private string id;

        /// <summary>
        /// Parent manager.
        /// </summary>
        public GameEventManagement GameManager;

        /// <summary>
        /// The list of nodes that can access this event.
        /// If we are in the node, we can activate the event. If we are not, we can't.
        /// If the list is empty, we do not care where we are as it is a global event.
        /// </summary>
        internal List<HSGNode> nodesBelongingTo = new List<HSGNode>();
        
        internal string currentState, anyState;

        /// <summary>
        /// Whether the character collided with this event at the time of the last update.
        /// </summary>
        internal bool CollidedLastUpdate { get { return collidedLastUpdate; } set { collidedLastUpdate = value; } }
        private bool collidedLastUpdate = false;

        /// <summary>
        /// Whether the character forward ray intersected this event at the time of the last update.
        /// </summary>
        internal bool OrientedLastUpdate { get { return intersectedLastUpdate; } set { intersectedLastUpdate = value; } }
        private bool intersectedLastUpdate = false;

        /// <summary>
        /// Events that occur when the character newly intersects the event.
        /// </summary>
        private Dictionary<string, List<GameEvent>> onIntersect = new Dictionary<string, List<GameEvent>>();

        /// <summary>
        /// Events that occur while the character intersects the event.
        /// </summary>
        private Dictionary<string, List<GameEvent>> whileIntersect = new Dictionary<string, List<GameEvent>>();

        /// <summary>
        /// Events that occur when the character stops intersecting the event.
        /// </summary>
        private Dictionary<string, List<GameEvent>> exitIntersect = new Dictionary<string, List<GameEvent>>();

        /// <summary>
        /// Events that occur when the character newly faces the event.
        /// </summary>
        private Dictionary<string, List<GameEvent>> onOrientation = new Dictionary<string, List<GameEvent>>();

        /// <summary>
        /// Events that occur while the character faces the event.
        /// </summary>
        private Dictionary<string, List<GameEvent>> whileOrientation = new Dictionary<string, List<GameEvent>>();

        /// <summary>
        /// Events that occur when the character stops facing the event.
        /// </summary>
        private Dictionary<string, List<GameEvent>> exitOrientation = new Dictionary<string, List<GameEvent>>();

        /// <summary>
        /// Add an event to the appropriate dictionary
        /// </summary>
        /// <param name="_gameState">A combination key that is in the form "State_Value"</param>
        /// <param name="_event">The event to add.</param>
        internal void AddEvent(string _gameState, GameEvent _event)
        {
            switch (_event.EventType)
            {
                case GameEventType.OnIntersect:
                    // Initialize if we need to
                    if (!this.onIntersect.ContainsKey(_gameState))
                    {
                        this.onIntersect.Add(_gameState, new List<GameEvent>());
                    }

                    // Add our event
                    this.onIntersect[_gameState].Add(_event);
                    break;
                case GameEventType.WhileIntersect:
                    // Initialize if we need to
                    if (!this.whileIntersect.ContainsKey(_gameState))
                    {
                        this.whileIntersect.Add(_gameState, new List<GameEvent>());
                    }

                    // Add our event
                    this.whileIntersect[_gameState].Add(_event);
                    break;
                case GameEventType.ExitIntersect:
                    // Initialize if we need to
                    if (!this.exitIntersect.ContainsKey(_gameState))
                    {
                        this.exitIntersect.Add(_gameState, new List<GameEvent>());
                    }

                    // Add our event
                    this.exitIntersect[_gameState].Add(_event);
                    break;
                case GameEventType.OnOrientation:
                    // Initialize if we need to
                    if (!this.onOrientation.ContainsKey(_gameState))
                    {
                        this.onOrientation.Add(_gameState, new List<GameEvent>());
                    }

                    // Add our event
                    this.onOrientation[_gameState].Add(_event);
                    break;
                case GameEventType.WhileOrientation:
                    // Initialize if we need to
                    if (!this.whileOrientation.ContainsKey(_gameState))
                    {
                        this.whileOrientation.Add(_gameState, new List<GameEvent>());
                    }

                    // Add our event
                    this.whileOrientation[_gameState].Add(_event);
                    break;
                case GameEventType.ExitOrientation:
                    // Initialize if we need to
                    if (!this.exitOrientation.ContainsKey(_gameState))
                    {
                        this.exitOrientation.Add(_gameState, new List<GameEvent>());
                    }

                    // Add our event
                    this.exitOrientation[_gameState].Add(_event);
                    break;
            }
        }

        /// <summary>
        /// Find events for a new intersetion.
        /// </summary>
        /// <param name="_gameState"></param>
        /// <param name="_gameTime"></param>
        /// <param name="_world"></param>
        /// <returns></returns>
        internal bool NewCollisionEvent(ref string _gameState, GameTime _gameTime, ref World _world)
        {
            if (!onIntersect.ContainsKey(_gameState))
            {
                return false;
            }

            // Check intersections
            for (int i = 0; i < onIntersect[_gameState].Count; i++)
            {
                switch (onIntersect[_gameState][i].EventAction)
                {
                    case GameEventAction.EnqueueText:
                        _world.textManager.EnqueueTextEvent(onIntersect[_gameState][i].EventArguments[0].ToString(), Convert.ToBoolean(onIntersect[_gameState][i].EventArguments[1], CultureInfo.InvariantCulture)); break;
                    case GameEventAction.DequeueText:
                        _world.textManager.DequeueTextEvent(onIntersect[_gameState][i].EventArguments[0].ToString(), Convert.ToBoolean(onIntersect[_gameState][i].EventArguments[1], CultureInfo.InvariantCulture)); break;
                    case GameEventAction.DisplayActionButton:
                        // We don't need to check intersect distance with collisions
                        GameManager.waitingOnInput = true;
                        GameManager.promptButton = onIntersect[_gameState][i].EventArguments[0].ToString();
                        GameManager.promptText.Remove(0, GameManager.promptText.Length);
                        GameManager.promptText.Append(onIntersect[_gameState][i].EventArguments[1].ToString());
                        GameManager.promptGameEvent.Clear();

                        // Skip the first three in the list of params
                        for (int j = 3; j < onIntersect[_gameState][i].EventArguments.Length; j++)
                        {
                            // Get information for event
                            GameEventAction action = (GameEventAction)Enum.Parse(typeof(GameEventAction), onIntersect[_gameState][i].EventArguments[j].ToString(), false);

                            int numArgs = Convert.ToInt32(onIntersect[_gameState][i].EventArguments[j + 1], CultureInfo.InvariantCulture);
                            object[] trimmedArgs = new object[numArgs];
                            Array.Copy(onIntersect[_gameState][i].EventArguments, j + 2, trimmedArgs, 0, trimmedArgs.Length);

                            // Add the event
                            GameManager.promptGameEvent.Add(new GameEvent(GameEventType.OnIntersect, action, trimmedArgs));

                            // Add the number of args from previous event added
                            j += numArgs + 1;
                        }
                        break;
                    case GameEventAction.LoadLevel:
                        LoadVariables loadVariables = new LoadVariables();
                        loadVariables.level = onIntersect[_gameState][i].EventArguments[0].ToString();
                        loadVariables.cameraPitch = (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[1].ToString(), CultureInfo.InvariantCulture);
                        loadVariables.cameraYaw = (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[2].ToString(), CultureInfo.InvariantCulture);
                        loadVariables.orientation = new Quaternion((float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[3].ToString(), CultureInfo.InvariantCulture),
                                                    (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[4].ToString(), CultureInfo.InvariantCulture),
                                                    (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[5].ToString(), CultureInfo.InvariantCulture),
                                                    (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[6].ToString(), CultureInfo.InvariantCulture));
                        loadVariables.position = new Vector3((float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[7].ToString(), CultureInfo.InvariantCulture),
                                                    (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[8].ToString(), CultureInfo.InvariantCulture),
                                                    (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[9].ToString(), CultureInfo.InvariantCulture));
                        //loadVariables.saved = DateTime.FromBinary(Convert.ToInt64(_world.TimePlayedAtStart));
                        loadVariables.saved = DateTime.FromFileTime(Convert.ToInt64(_world.TimePlayedAtStart, CultureInfo.InvariantCulture));
                        loadVariables.timePlayed = _world.TimePlayed;
                        loadVariables.totalRotationY = (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[10].ToString(), CultureInfo.InvariantCulture);
                        loadVariables.gameState = GlobalVariables.loadVariables.gameState;
                        loadVariables.modelPositioning = GlobalVariables.loadVariables.modelPositioning;
                        LoadingScreen.Load(_world.screenManager, true, _world.screenManager.GetScreens()[0].ControllingPlayer.Value,
                            new GameplayScreen(_world.TimePlayed, loadVariables));
                        break;
                    case GameEventAction.CameraPath:
                        _world.levelManager.FPSCamera.SetCameraMode(CameraMode.Manual, _world.levelManager.Character.CharacterController.Body, new Vector3(0, 2.0f, 0), true, 15);
                        _world.levelManager.journal.IsShown = false;
                        List<CameraNode> cameraPositions = new List<CameraNode>();
                        List<CameraNode> cameraLookAts = new List<CameraNode>();
                        int numPositions = (int)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[4], CultureInfo.InvariantCulture);
                        for (int j = 5; j < (numPositions * 4) + 5; j += 4)
                        {
                            cameraPositions.Add(new CameraNode(new Vector3((float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[j], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[j + 1], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[j + 2], CultureInfo.InvariantCulture)), (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[j + 3], CultureInfo.InvariantCulture)));
                        }
                        int numLookAts = (int)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[(numPositions * 4) + 5], CultureInfo.InvariantCulture);
                        for (int j = (numPositions * 4) + 6; j < (numLookAts * 4) + (numPositions * 4) + 6; j += 4)
                        {
                            cameraLookAts.Add(new CameraNode(new Vector3((float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[j], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[j + 1], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[j + 2], CultureInfo.InvariantCulture)), (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[j + 3], CultureInfo.InvariantCulture)));
                        }
                        _world.levelManager.FPSCamera.InitCurves((CurveLoopType)Enum.Parse(typeof(CurveLoopType), onIntersect[_gameState][i].EventArguments[0].ToString(), false),
                                                                (CurveLoopType)Enum.Parse(typeof(CurveLoopType), onIntersect[_gameState][i].EventArguments[1].ToString(), false),
                                                                (CurveLoopType)Enum.Parse(typeof(CurveLoopType), onIntersect[_gameState][i].EventArguments[2].ToString(), false),
                                                                (CurveLoopType)Enum.Parse(typeof(CurveLoopType), onIntersect[_gameState][i].EventArguments[3].ToString(), false),
                                                                cameraPositions, cameraLookAts);
                        break;
                    case GameEventAction.ChangeGameState:
                        GlobalVariables.loadVariables.gameState[onIntersect[_gameState][i].EventArguments[0].ToString()] = onIntersect[_gameState][i].EventArguments[1].ToString();
                        GameManager.RefreshGameStates();
                        _world.levelManager.ShowJournalUpdating = Convert.ToBoolean(onIntersect[_gameState][i].EventArguments[2], CultureInfo.InvariantCulture);
                        break;
                    case GameEventAction.Audio:
                        throw new NotImplementedException();
                    case GameEventAction.ChangeAnimation:
                        _world.levelManager.Animations[onIntersect[_gameState][i].EventArguments[0].ToString()].Play(new BoneAnimation(_world.levelManager.AnimatedModels[onIntersect[_gameState][i].EventArguments[0].ToString()].modelSkeleton,
                                                                                                                                       _world.levelManager.AnimatedModels[onIntersect[_gameState][i].EventArguments[0].ToString()].DisplayModel.GetAnimation(onIntersect[_gameState][i].EventArguments[1].ToString())));
                        break;
                    case GameEventAction.MoveModel:
                        if (onIntersect[_gameState][i].EventArguments[1].ToString() == "Absolute")
                        {
                            if (_world.levelManager.AnimatedModels.ContainsKey(onIntersect[_gameState][i].EventArguments[0].ToString()))
                            {
                                _world.levelManager.AnimatedModels[onIntersect[_gameState][i].EventArguments[0].ToString()].SetPosition(new Vector3((float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[2], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[3], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[4], CultureInfo.InvariantCulture)));
                                _world.levelManager.AnimatedModels[onIntersect[_gameState][i].EventArguments[0].ToString()].SetRotation(new Vector3((float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[5], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[6], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[7], CultureInfo.InvariantCulture)));
                            }
                            else if (_world.levelManager.StaticModels.ContainsKey(onIntersect[_gameState][i].EventArguments[0].ToString()))
                            {
                                _world.levelManager.StaticModels[onIntersect[_gameState][i].EventArguments[0].ToString()].SetPosition(new Vector3((float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[2], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[3], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[4], CultureInfo.InvariantCulture)));
                                _world.levelManager.StaticModels[onIntersect[_gameState][i].EventArguments[0].ToString()].SetRotation(new Vector3((float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[5], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[6], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[7], CultureInfo.InvariantCulture)));
                            }
                        }
                        else if (onIntersect[_gameState][i].EventArguments[1].ToString() == "Relative")
                        {
                            Vector3 pos = _world.levelManager.AnimatedModels[onIntersect[_gameState][i].EventArguments[0].ToString()].Position;
                            Vector3 rot = _world.levelManager.AnimatedModels[onIntersect[_gameState][i].EventArguments[0].ToString()].Rotation;
                            if (_world.levelManager.AnimatedModels.ContainsKey(onIntersect[_gameState][i].EventArguments[0].ToString()))
                            {
                                _world.levelManager.AnimatedModels[onIntersect[_gameState][i].EventArguments[0].ToString()].SetPosition(new Vector3((float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[2], CultureInfo.InvariantCulture) + pos.X, (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[3], CultureInfo.InvariantCulture) + pos.Y, (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[4], CultureInfo.InvariantCulture) + pos.Z));
                                _world.levelManager.AnimatedModels[onIntersect[_gameState][i].EventArguments[0].ToString()].SetRotation(new Vector3((float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[5], CultureInfo.InvariantCulture) + rot.X, (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[6], CultureInfo.InvariantCulture) + rot.Y, (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[7], CultureInfo.InvariantCulture) + rot.Z));
                            }
                            else if (_world.levelManager.StaticModels.ContainsKey(onIntersect[_gameState][i].EventArguments[0].ToString()))
                            {
                                _world.levelManager.StaticModels[onIntersect[_gameState][i].EventArguments[0].ToString()].SetPosition(new Vector3((float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[2], CultureInfo.InvariantCulture) + pos.X, (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[3], CultureInfo.InvariantCulture) + pos.Y, (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[4], CultureInfo.InvariantCulture) + pos.Z));
                                _world.levelManager.StaticModels[onIntersect[_gameState][i].EventArguments[0].ToString()].SetRotation(new Vector3((float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[5], CultureInfo.InvariantCulture) + rot.X, (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[6], CultureInfo.InvariantCulture) + rot.Y, (float)Convert.ToDouble(onIntersect[_gameState][i].EventArguments[7], CultureInfo.InvariantCulture) + rot.Z));
                            }
                        }
                        break;
                    case GameEventAction.ShowLetter:
                        _world.textManager.EnqueueTextEvent(onIntersect[_gameState][i].EventArguments[0].ToString(), false);
                        _world.textManager.SetLetter(onIntersect[_gameState][i].EventArguments[0].ToString());
                        AudioManager.soundEffects["pageflip12"].PlaySound(1.0f, 0.0f, 0.0f, false);
                        break;
                }
            }

            return true;
        }

        /// <summary>
        /// Find events for a new orientation.
        /// </summary>
        /// <param name="_gameState"></param>
        /// <param name="_gameTime"></param>
        /// <param name="_world"></param>
        /// <returns></returns>
        internal bool NewOrientationEvent(ref string _gameState, GameTime _gameTime, ref World _world)
        {
            if (!onOrientation.ContainsKey(_gameState))
            {
                return false;
            }

            // Check orientations
            for (int i = 0; i < onOrientation[_gameState].Count; i++)
            {
                switch (onOrientation[_gameState][i].EventAction)
                {
                    case GameEventAction.EnqueueText:
                        _world.textManager.EnqueueTextEvent(onOrientation[_gameState][i].EventArguments[0].ToString(), Convert.ToBoolean(onOrientation[_gameState][i].EventArguments[1], CultureInfo.InvariantCulture));
                        break;
                    case GameEventAction.DequeueText:
                        _world.textManager.DequeueTextEvent(onOrientation[_gameState][i].EventArguments[0].ToString(), Convert.ToBoolean(onOrientation[_gameState][i].EventArguments[1], CultureInfo.InvariantCulture));
                        break;
                    case GameEventAction.DisplayActionButton:
                        GameManager.waitingOnInput = true;
                        GameManager.promptButton = onOrientation[_gameState][i].EventArguments[0].ToString();
                        GameManager.promptText.Remove(0, GameManager.promptText.Length);
                        GameManager.promptText.Append(onOrientation[_gameState][i].EventArguments[1].ToString());
                        GameManager.promptGameEvent.Clear();

                        // Skip the first three in the list of params
                        for (int j = 3; j < onOrientation[_gameState][i].EventArguments.Length; j++)
                        {
                            // Get information for event
                            GameEventAction action = (GameEventAction)Enum.Parse(typeof(GameEventAction), onOrientation[_gameState][i].EventArguments[j].ToString(), false);
                            int numArgs = Convert.ToInt32(onOrientation[_gameState][i].EventArguments[j + 1], CultureInfo.InvariantCulture);
                            object[] trimmedArgs = new object[numArgs];
                            Array.Copy(onOrientation[_gameState][i].EventArguments, j + 2, trimmedArgs, 0, trimmedArgs.Length);

                            // Add the event
                            GameManager.promptGameEvent.Add(new GameEvent(GameEventType.OnOrientation, action, trimmedArgs));

                            // Add the number of args from previous event added
                            j += numArgs + 1;
                        }
                        break;
                    case GameEventAction.LoadLevel:
                        throw new NotImplementedException();
                    case GameEventAction.CameraPath:
                        throw new NotImplementedException();
                    case GameEventAction.ChangeGameState:
                        GlobalVariables.loadVariables.gameState[onOrientation[_gameState][i].EventArguments[0].ToString()] = onOrientation[_gameState][i].EventArguments[1].ToString();
                        GameManager.RefreshGameStates();
                        _world.levelManager.ShowJournalUpdating =  Convert.ToBoolean(onOrientation[_gameState][i].EventArguments[2], CultureInfo.InvariantCulture);;
                        break;
                    case GameEventAction.Audio:
                        throw new NotImplementedException();
                    case GameEventAction.ChangeAnimation:
                        _world.levelManager.Animations[onOrientation[_gameState][i].EventArguments[0].ToString()].Play(new BoneAnimation(_world.levelManager.AnimatedModels[onOrientation[_gameState][i].EventArguments[0].ToString()].modelSkeleton,
                                                                                                               _world.levelManager.AnimatedModels[onOrientation[_gameState][i].EventArguments[0].ToString()].DisplayModel.GetAnimation(onOrientation[_gameState][i].EventArguments[1].ToString())));
                        break;
                    case GameEventAction.MoveModel:
                        if (onOrientation[_gameState][i].EventArguments[1].ToString() == "Absolute")
                        {
                            _world.levelManager.AnimatedModels[onOrientation[_gameState][i].EventArguments[0].ToString()].SetPosition(new Vector3((float)Convert.ToDouble(onOrientation[_gameState][i].EventArguments[2], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onOrientation[_gameState][i].EventArguments[3], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onOrientation[_gameState][i].EventArguments[4], CultureInfo.InvariantCulture)));
                            _world.levelManager.AnimatedModels[onOrientation[_gameState][i].EventArguments[0].ToString()].SetRotation(new Vector3((float)Convert.ToDouble(onOrientation[_gameState][i].EventArguments[5], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onOrientation[_gameState][i].EventArguments[6], CultureInfo.InvariantCulture), (float)Convert.ToDouble(onOrientation[_gameState][i].EventArguments[7], CultureInfo.InvariantCulture)));
                        }
                        else if (onOrientation[_gameState][i].EventArguments[1].ToString() == "Relative")
                        {
                            Vector3 pos = _world.levelManager.AnimatedModels[onOrientation[_gameState][i].EventArguments[0].ToString()].Position;
                            Vector3 rot = _world.levelManager.AnimatedModels[onOrientation[_gameState][i].EventArguments[0].ToString()].Rotation;

                            _world.levelManager.AnimatedModels[onOrientation[_gameState][i].EventArguments[0].ToString()].SetPosition(new Vector3((float)Convert.ToDouble(onOrientation[_gameState][i].EventArguments[2], CultureInfo.InvariantCulture) + pos.X, (float)Convert.ToDouble(onOrientation[_gameState][i].EventArguments[3], CultureInfo.InvariantCulture) + pos.Y, (float)Convert.ToDouble(onOrientation[_gameState][i].EventArguments[4], CultureInfo.InvariantCulture) + pos.Z));
                            _world.levelManager.AnimatedModels[onOrientation[_gameState][i].EventArguments[0].ToString()].SetRotation(new Vector3((float)Convert.ToDouble(onOrientation[_gameState][i].EventArguments[5], CultureInfo.InvariantCulture) + rot.X, (float)Convert.ToDouble(onOrientation[_gameState][i].EventArguments[6], CultureInfo.InvariantCulture) + rot.Y, (float)Convert.ToDouble(onOrientation[_gameState][i].EventArguments[7], CultureInfo.InvariantCulture) + rot.Z));
                        }
                        break;
                    case GameEventAction.ShowLetter:
                        throw new NotImplementedException();
                }
            }

            return true;
        }

        /// <summary>
        /// Find events for an existin collision.
        /// </summary>
        /// <param name="_gameState"></param>
        /// <param name="_gameTime"></param>
        /// <param name="_world"></param>
        /// <returns></returns>
        internal bool WhileCollisonEvent(ref string _gameState, GameTime _gameTime, ref World _world)
        {
            if (!whileIntersect.ContainsKey(_gameState))
            {
                return false;
            }

            // Check intersections
            for (int i = 0; i < whileIntersect[_gameState].Count; i++)
            {
                switch (whileIntersect[_gameState][i].EventAction)
                {
                    case GameEventAction.EnqueueText:
                        _world.textManager.EnqueueTextEvent(whileIntersect[_gameState][i].EventArguments[0].ToString(), Convert.ToBoolean(whileIntersect[_gameState][i].EventArguments[1], CultureInfo.InvariantCulture)); break;
                    case GameEventAction.DequeueText:
                    case GameEventAction.DisplayActionButton:
                    case GameEventAction.LoadLevel:
                    case GameEventAction.CameraPath:
                        throw new NotImplementedException();
                    case GameEventAction.ChangeGameState:
                        GlobalVariables.loadVariables.gameState[whileIntersect[_gameState][i].EventArguments[0].ToString()] = whileIntersect[_gameState][i].EventArguments[1].ToString();
                        GameManager.RefreshGameStates();
                        _world.levelManager.ShowJournalUpdating = Convert.ToBoolean(whileIntersect[_gameState][i].EventArguments[2], CultureInfo.InvariantCulture);
                        break;
                    case GameEventAction.Audio:
                        throw new NotImplementedException();
                    case GameEventAction.ChangeAnimation:
                    case GameEventAction.MoveModel:
                        throw new NotImplementedException();
                    case GameEventAction.ShowLetter:
                        throw new NotImplementedException();
                }
            }

            return true;
        }

        /// <summary>
        /// Find events for an existin orientation.
        /// </summary>
        /// <param name="_gameState"></param>
        /// <param name="_gameTime"></param>
        /// <param name="_world"></param>
        /// <returns></returns>
        internal bool WhileOrientationEvent(ref string _gameState, GameTime _gameTime, ref World _world)
        {
            if (!whileOrientation.ContainsKey(_gameState))
            {
                return false;
            }

            // Check orientations
            for (int i = 0; i < whileOrientation[_gameState].Count; i++)
            {
                switch (whileOrientation[_gameState][i].EventAction)
                {
                    case GameEventAction.EnqueueText:
                    case GameEventAction.DequeueText:
                    case GameEventAction.DisplayActionButton:
                    case GameEventAction.LoadLevel:
                    case GameEventAction.CameraPath:
                    case GameEventAction.ChangeGameState:
                        throw new NotImplementedException();
                    case GameEventAction.Audio:
                    case GameEventAction.ChangeAnimation:
                    case GameEventAction.MoveModel:
                        throw new NotImplementedException();
                    case GameEventAction.ShowLetter:
                        throw new NotImplementedException();
                }
            }

            return true;
        }

        /// <summary>
        /// Find events for exiting collisions.
        /// </summary>
        /// <param name="_gameState"></param>
        /// <param name="_gameTime"></param>
        /// <param name="_world"></param>
        /// <returns></returns>
        internal bool ExitCollisionEvent(ref string _gameState, GameTime _gameTime, ref World _world)
        {
            if (!exitIntersect.ContainsKey(_gameState))
            {
                return false;
            }

            // Check intersections
            for (int i = 0; i < exitIntersect[_gameState].Count; i++)
            {
                switch (exitIntersect[_gameState][i].EventAction)
                {
                    case GameEventAction.EnqueueText:
                        _world.textManager.EnqueueTextEvent(exitIntersect[_gameState][i].EventArguments[0].ToString(), Convert.ToBoolean(exitIntersect[_gameState][i].EventArguments[1], CultureInfo.InvariantCulture));
                        break;
                    case GameEventAction.DequeueText:
                        _world.textManager.DequeueTextEvent(exitIntersect[_gameState][i].EventArguments[0].ToString(), Convert.ToBoolean(exitIntersect[_gameState][i].EventArguments[1], CultureInfo.InvariantCulture));
                        break;
                    case GameEventAction.DisplayActionButton:
                        GameManager.waitingOnInput = false;
                        GameManager.promptGameEvent.Clear();
                        break;
                    case GameEventAction.LoadLevel:
                    case GameEventAction.CameraPath:
                        throw new NotImplementedException();
                    case GameEventAction.ChangeGameState:
                        GlobalVariables.loadVariables.gameState[exitIntersect[_gameState][i].EventArguments[0].ToString()] = exitIntersect[_gameState][i].EventArguments[1].ToString();
                        GameManager.RefreshGameStates();
                        _world.levelManager.ShowJournalUpdating =  Convert.ToBoolean(exitIntersect[_gameState][i].EventArguments[2], CultureInfo.InvariantCulture);;
                        break;
                    case GameEventAction.Audio:
                        throw new NotImplementedException();
                    case GameEventAction.ChangeAnimation:
                        _world.levelManager.Animations[exitIntersect[_gameState][i].EventArguments[0].ToString()].Play(new BoneAnimation(_world.levelManager.AnimatedModels[exitIntersect[_gameState][i].EventArguments[0].ToString()].modelSkeleton,
                                                                                                               _world.levelManager.AnimatedModels[exitIntersect[_gameState][i].EventArguments[0].ToString()].DisplayModel.GetAnimation(exitIntersect[_gameState][i].EventArguments[1].ToString())));
                        break;
                    case GameEventAction.MoveModel:
                        if (exitIntersect[_gameState][i].EventArguments[1].ToString() == "Absolute")
                        {
                            _world.levelManager.AnimatedModels[exitIntersect[_gameState][i].EventArguments[0].ToString()].SetPosition(new Vector3((float)Convert.ToDouble(exitIntersect[_gameState][i].EventArguments[2], CultureInfo.InvariantCulture), (float)Convert.ToDouble(exitIntersect[_gameState][i].EventArguments[3], CultureInfo.InvariantCulture), (float)Convert.ToDouble(exitIntersect[_gameState][i].EventArguments[4], CultureInfo.InvariantCulture)));
                            _world.levelManager.AnimatedModels[exitIntersect[_gameState][i].EventArguments[0].ToString()].SetRotation(new Vector3((float)Convert.ToDouble(exitIntersect[_gameState][i].EventArguments[5], CultureInfo.InvariantCulture), (float)Convert.ToDouble(exitIntersect[_gameState][i].EventArguments[6], CultureInfo.InvariantCulture), (float)Convert.ToDouble(exitIntersect[_gameState][i].EventArguments[7], CultureInfo.InvariantCulture)));
                        }
                        else if (exitIntersect[_gameState][i].EventArguments[1].ToString() == "Relative")
                        {
                            Vector3 pos = _world.levelManager.AnimatedModels[exitIntersect[_gameState][i].EventArguments[0].ToString()].Position;
                            Vector3 rot = _world.levelManager.AnimatedModels[exitIntersect[_gameState][i].EventArguments[0].ToString()].Rotation;

                            _world.levelManager.AnimatedModels[exitIntersect[_gameState][i].EventArguments[0].ToString()].SetPosition(new Vector3((float)Convert.ToDouble(exitIntersect[_gameState][i].EventArguments[2], CultureInfo.InvariantCulture) + pos.X, (float)Convert.ToDouble(exitIntersect[_gameState][i].EventArguments[3], CultureInfo.InvariantCulture) + pos.Y, (float)Convert.ToDouble(exitIntersect[_gameState][i].EventArguments[4], CultureInfo.InvariantCulture) + pos.Z));
                            _world.levelManager.AnimatedModels[exitIntersect[_gameState][i].EventArguments[0].ToString()].SetRotation(new Vector3((float)Convert.ToDouble(exitIntersect[_gameState][i].EventArguments[5], CultureInfo.InvariantCulture) + rot.X, (float)Convert.ToDouble(exitIntersect[_gameState][i].EventArguments[6], CultureInfo.InvariantCulture) + rot.Y, (float)Convert.ToDouble(exitIntersect[_gameState][i].EventArguments[7], CultureInfo.InvariantCulture) + rot.Z));
                        }
                        break;
                    case GameEventAction.ShowLetter:
                        throw new NotImplementedException();
                }
            }

            return true;
        }

        /// <summary>
        /// Find events for exiting orientation.
        /// </summary>
        /// <param name="_gameState"></param>
        /// <param name="_gameTime"></param>
        /// <param name="_world"></param>
        /// <returns></returns>
        internal bool ExitOrientationEvent(ref string _gameState, GameTime _gameTime, ref World _world)
        {
            if (!exitOrientation.ContainsKey(_gameState))
            {
                return false;
            }

            // Check orientations
            for (int i = 0; i < exitOrientation[_gameState].Count; i++)
            {
                switch (exitOrientation[_gameState][i].EventAction)
                {
                    case GameEventAction.EnqueueText:
                        _world.textManager.EnqueueTextEvent(exitOrientation[_gameState][i].EventArguments[0].ToString(), Convert.ToBoolean(exitOrientation[_gameState][i].EventArguments[1], CultureInfo.InvariantCulture));
                        break;
                    case GameEventAction.DequeueText:
                        _world.textManager.DequeueTextEvent(exitOrientation[_gameState][i].EventArguments[0].ToString(), Convert.ToBoolean(exitOrientation[_gameState][i].EventArguments[1], CultureInfo.InvariantCulture));
                        break;
                    case GameEventAction.DisplayActionButton:
                        GameManager.waitingOnInput = false;
                        GameManager.promptGameEvent.Clear();
                        break;
                    case GameEventAction.LoadLevel:
                    case GameEventAction.CameraPath:
                    case GameEventAction.ChangeGameState:
                        throw new NotImplementedException();
                    case GameEventAction.Audio:
                        throw new NotImplementedException();
                    case GameEventAction.ChangeAnimation:
                        _world.levelManager.Animations[exitOrientation[_gameState][i].EventArguments[0].ToString()].Play(new BoneAnimation(_world.levelManager.AnimatedModels[exitOrientation[_gameState][i].EventArguments[0].ToString()].modelSkeleton,
                                                                                                               _world.levelManager.AnimatedModels[exitOrientation[_gameState][i].EventArguments[0].ToString()].DisplayModel.GetAnimation(exitOrientation[_gameState][i].EventArguments[1].ToString())));
                        break;
                    case GameEventAction.MoveModel:
                        if (exitOrientation[_gameState][i].EventArguments[1].ToString() == "Absolute")
                        {
                            _world.levelManager.AnimatedModels[exitOrientation[_gameState][i].EventArguments[0].ToString()].SetPosition(new Vector3((float)Convert.ToDouble(exitOrientation[_gameState][i].EventArguments[2], CultureInfo.InvariantCulture), (float)Convert.ToDouble(exitOrientation[_gameState][i].EventArguments[3], CultureInfo.InvariantCulture), (float)Convert.ToDouble(exitOrientation[_gameState][i].EventArguments[4], CultureInfo.InvariantCulture)));
                            _world.levelManager.AnimatedModels[exitOrientation[_gameState][i].EventArguments[0].ToString()].SetRotation(new Vector3((float)Convert.ToDouble(exitOrientation[_gameState][i].EventArguments[5], CultureInfo.InvariantCulture), (float)Convert.ToDouble(exitOrientation[_gameState][i].EventArguments[6], CultureInfo.InvariantCulture), (float)Convert.ToDouble(exitOrientation[_gameState][i].EventArguments[7], CultureInfo.InvariantCulture)));
                        }
                        else if (exitOrientation[_gameState][i].EventArguments[1].ToString() == "Relative")
                        {
                            Vector3 pos = _world.levelManager.AnimatedModels[exitOrientation[_gameState][i].EventArguments[0].ToString()].Position;
                            Vector3 rot = _world.levelManager.AnimatedModels[exitOrientation[_gameState][i].EventArguments[0].ToString()].Rotation;

                            _world.levelManager.AnimatedModels[exitOrientation[_gameState][i].EventArguments[0].ToString()].SetPosition(new Vector3((float)Convert.ToDouble(exitOrientation[_gameState][i].EventArguments[2], CultureInfo.InvariantCulture) + pos.X, (float)Convert.ToDouble(exitOrientation[_gameState][i].EventArguments[3], CultureInfo.InvariantCulture) + pos.Y, (float)Convert.ToDouble(exitOrientation[_gameState][i].EventArguments[4], CultureInfo.InvariantCulture) + pos.Z));
                            _world.levelManager.AnimatedModels[exitOrientation[_gameState][i].EventArguments[0].ToString()].SetRotation(new Vector3((float)Convert.ToDouble(exitOrientation[_gameState][i].EventArguments[5], CultureInfo.InvariantCulture) + rot.X, (float)Convert.ToDouble(exitOrientation[_gameState][i].EventArguments[6], CultureInfo.InvariantCulture) + rot.Y, (float)Convert.ToDouble(exitOrientation[_gameState][i].EventArguments[7], CultureInfo.InvariantCulture) + rot.Z));
                        }
                        break;
                    case GameEventAction.ShowLetter:
                        throw new NotImplementedException();
                }
            }

            return true;
        }
    }

    /// <summary>
    /// A box collision based event trigger.
    /// </summary>
    internal sealed class EventBox : EventObject
    {
        /// <summary>
        /// Box collision event.
        /// </summary>
        internal BoundingBox Box { get { return box; } set { box = value; } }
        private BoundingBox box;

        /// <summary>
        /// When an event is part of a model, we translate based on the original box position.
        /// If we do not track the original position, every time we move the model the box is assigned to,
        /// the values grow exponentially larger.
        /// </summary>
        internal BoundingBox OriginalBox { get { return originalBox; } }
        private BoundingBox originalBox;

        /// <summary>
        /// Create an event.
        /// </summary>
        /// <param name="_id"></param>
        /// <param name="_box"></param>
        public EventBox(string _id, GameEventManagement _gm, BoundingBox _box, List<HSGNode> _nodes)
        {
            ID = _id;
            GameManager = _gm;
            box = new BoundingBox(_box.Min, _box.Max);
            originalBox = new BoundingBox(_box.Min, _box.Max);

            for (int i = 0; i < _nodes.Count; i++)
            {
                nodesBelongingTo.Add(_nodes[i]);
            }
        }

        /// <summary>
        /// Check if we trigger an event.
        /// </summary>
        /// <param name="_box">The character's body bounding box.</param>
        /// <param name="_gameState"></param>
        /// <param name="_gameTime"></param>
        public void Update(BoundingBox _box, GameTime _gameTime, float? _currentOrientationRayDist, ref World _world, ref bool _collidedLastUpdate, ref bool _orientedLastUpdate)
        {
            //DebugSystem.Instance.EventDisplayer.EnqueueDisplayEvent("Checking box: " + this.ID);
            //DebugShapeRenderer.AddBoundingBox(this.box, Color.CornflowerBlue);

            // Check collision events
            if (_box.Intersects(this.box))
            {
                if (!CollidedLastUpdate)
                {
                    // Do new intersect events, once for the current state, once for the "all" state
                    NewCollisionEvent(ref currentState, _gameTime, ref _world);
                    NewCollisionEvent(ref anyState, _gameTime, ref _world);

                    // We intersected this frame
                    _collidedLastUpdate = true;
                }

                // Do intersect events, once for the current state, once for the "all" state
                WhileCollisonEvent(ref currentState, _gameTime, ref _world);
                WhileCollisonEvent(ref anyState, _gameTime, ref _world);
            }
            else if (CollidedLastUpdate)
            {
                // Do exiting events, once for the current state, once for the "all" state
                ExitCollisionEvent(ref currentState, _gameTime, ref _world);
                ExitCollisionEvent(ref anyState, _gameTime, ref _world);

                // We did not intersect this update
                _collidedLastUpdate = false;
            }

            // Check orientation events
            if (_currentOrientationRayDist.HasValue &&
                _currentOrientationRayDist < GlobalVariables.cRayDistance)
            {
                //DebugSystem.Instance.EventDisplayer.EnqueueDisplayEvent("Oriented: " + this.ID + " with " + _currentOrientationRayDist.ToString() + "/" + _bestOrientationRayDist.ToString());
                //DebugShapeRenderer.AddBoundingBox(this.box, Color.HotPink);

                if (!OrientedLastUpdate)
                {
                    // Do new intersect events, once for the current state, once for the "all" state
                    NewOrientationEvent(ref currentState, _gameTime, ref _world);
                    NewOrientationEvent(ref anyState, _gameTime, ref _world);

                    // We intersected this frame
                    _orientedLastUpdate = true;
                }

                // Do intersect events, once for the current state, once for the "all" state
                WhileOrientationEvent(ref currentState, _gameTime, ref _world);
                WhileOrientationEvent(ref anyState, _gameTime, ref _world);
            }
            else if (OrientedLastUpdate)
            {
                // Do exiting events, once for the current state, once for the "all" state
                ExitOrientationEvent(ref currentState, _gameTime, ref _world);
                ExitOrientationEvent(ref anyState, _gameTime, ref _world);

                // We did not intersect this update
                _orientedLastUpdate = false;
            }
        }
    }

    /// <summary>
    /// A sphere collision based event trigger.
    /// </summary>
    internal sealed class EventSphere : EventObject
    {
        /// <summary>
        /// Spherical collision event.
        /// </summary>
        internal BoundingSphere Sphere { get { return sphere; } set { sphere = value; } }
        private BoundingSphere sphere;

        /// <summary>
        /// Create an event.
        /// </summary>
        /// <param name="_id"></param>
        /// <param name="_sphere"></param>
        internal EventSphere(string _id, GameEventManagement _gm, BoundingSphere _sphere, List<HSGNode> _nodes)
        {
            ID = _id;
            GameManager = _gm;
            sphere = new BoundingSphere(_sphere.Center, _sphere.Radius);

            for (int i = 0; i < _nodes.Count; i++)
            {
                nodesBelongingTo.Add(_nodes[i]);
            }
        }

        /// <summary>
        /// Check if we trigger an event.
        /// </summary>
        /// <param name="_box">The character's body bounding box.</param>
        /// <param name="_gameState"></param>
        /// <param name="_gameTime"></param>
        internal void Update(BoundingBox _box, GameTime _gameTime, float? _currentOrientationRayDist, ref World _world, ref bool _collidedLastUpdate, ref bool _orientedLastUpdate)
        {
            //DebugSystem.Instance.EventDisplayer.EnqueueDisplayEvent("Checking sphere: " + this.ID);
            //DebugShapeRenderer.AddBoundingSphere(this.sphere, Color.CornflowerBlue);

            // Check collision events
            if (_box.Intersects(sphere))
            {
                if (!CollidedLastUpdate)
                {
                    // Do new intersect events, once for the current state, once for the "all" state
                    NewCollisionEvent(ref currentState, _gameTime, ref _world);
                    NewCollisionEvent(ref anyState, _gameTime, ref _world);

                    // We intersected this frame
                    _collidedLastUpdate = true;
                }

                // Do intersect events, once for the current state, once for the "all" state
                WhileCollisonEvent(ref currentState, _gameTime, ref _world);
                WhileCollisonEvent(ref anyState, _gameTime, ref _world);
            }
            else if (CollidedLastUpdate)
            {
                // Do exiting events, once for the current state, once for the "all" state
                ExitCollisionEvent(ref currentState, _gameTime, ref _world);
                ExitCollisionEvent(ref anyState, _gameTime, ref _world);

                // We did not intersect this update
                _collidedLastUpdate = false;
            }

            // Check orientation events
            if (_currentOrientationRayDist.HasValue &&
                _currentOrientationRayDist < GlobalVariables.cRayDistance)
            {
                //DebugSystem.Instance.EventDisplayer.EnqueueDisplayEvent("Oriented: " + this.ID + " with " + _currentOrientationRayDist.ToString() + "/" + _bestOrientationRayDist.ToString());
                //DebugShapeRenderer.AddBoundingSphere(this.sphere, Color.HotPink);

                if (!OrientedLastUpdate)
                {
                    // Do new intersect events, once for the current state, once for the "all" state
                    NewOrientationEvent(ref currentState, _gameTime, ref _world);
                    NewOrientationEvent(ref anyState, _gameTime, ref _world);

                    // We intersected this frame
                    _orientedLastUpdate = true;
                }

                // Do intersect events, once for the current state, once for the "all" state
                WhileOrientationEvent(ref currentState, _gameTime, ref _world);
                WhileOrientationEvent(ref anyState, _gameTime, ref _world);
            }
            else if (OrientedLastUpdate)
            {
                // Do exiting events, once for the current state, once for the "all" state
                ExitOrientationEvent(ref currentState, _gameTime, ref _world);
                ExitOrientationEvent(ref anyState, _gameTime, ref _world);

                // We did not intersect this update
                _orientedLastUpdate = false;
            }
        }
    }
}